package com.ligoudan.ui;

import com.ligoudan.core.GenContext;
import com.ligoudan.model.GenImageModel;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public class ApplicationFrame extends JFrame {
    private static final long serialVersionUID = 314253059590404161L;

    private static final Logger LOGGER = Logger.getLogger(ApplicationFrame.class.getName());

    private final GenContext context;

    public ApplicationFrame(GenContext context) {
        super();
        this.context = context;
    }

    public void start() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setTitle("长图拼接生成助手");
        this.setSize(550, 300);
        this.setLocationRelativeTo(null);
        this.setIconImage(new ImageIcon(getFrameIcon()).getImage());
        this.setResizable(false);

        JPanel cardPanel = new JPanel();
        cardPanel.setLayout(new CardLayout());

        FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT);
        JPanel operatePanel = new JPanel();
        operatePanel.setLayout(flowLayout);

        JLabel genLabel = new JLabel();

        JButton chooseImageBtn = new JButton("选择图片");
        chooseImageBtn.addActionListener(actionEvent -> handleChooseImages(genLabel));

        JButton genImageBtn = new JButton("开始生成");
        genImageBtn.addActionListener(actionEvent -> handleGenLongImage());

        operatePanel.add(chooseImageBtn);
        operatePanel.add(genImageBtn);
        operatePanel.add(genLabel);


        JPanel filePathPanel = new JPanel();
        filePathPanel.setLayout(flowLayout);

        JLabel filePathLabel = new JLabel("保存路径：");
        JTextField filePathTextField = new JTextField(18);
        filePathTextField.setText(context.getGenImageModel().getFilePath());
        filePathTextField.setEditable(false);
        JButton filePathBtn = new JButton("选择");
        filePathBtn.addActionListener(e -> {
            JFileChooser fc = new JFileChooser(context.getGenImageModel().getFilePath());
            fc.setDialogTitle("选择保存路径......");
            fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            fc.setMultiSelectionEnabled(false);
            int flag = fc.showOpenDialog(this);
            if (flag == JFileChooser.APPROVE_OPTION) {
                File saveLocation = fc.getSelectedFile();
                context.getGenImageModel().setFilePath(saveLocation.getAbsolutePath());
                filePathTextField.setText(saveLocation.getAbsolutePath());
            }
        });

        filePathPanel.add(filePathLabel);
        filePathPanel.add(filePathTextField);
        filePathPanel.add(filePathBtn);

        JPanel filenamePanel = new JPanel(flowLayout);
        JLabel filenameLabel = new JLabel("文件名称：");
        JTextField filenameTextField = new JTextField(13);
        filenameTextField.setText(context.getGenImageModel().getFilename());
        filenameTextField.setName("filenameTextField");
        context.getFormComponents().add(filenameTextField);
        filenamePanel.add(filenameLabel);
        filenamePanel.add(filenameTextField);

        JPanel imageWidthPanel = new JPanel(flowLayout);
        JLabel imageWidthLabel = new JLabel("图片宽度：");
        JTextField imageWidthTextField = new JTextField(5);
        imageWidthTextField.setDocument(new NumberDocument());
        imageWidthTextField.setText(context.getGenImageModel().getMaxWidth() + "");
        imageWidthTextField.setName("imageWidthTextField");
        context.getFormComponents().add(imageWidthTextField);

        JLabel imageWidthDescLabel = new JLabel("(单位：px)");

        imageWidthPanel.add(imageWidthLabel);
        imageWidthPanel.add(imageWidthTextField);
        imageWidthPanel.add(imageWidthDescLabel);

//        this.setLayout(null);
        int yOffset = 10;
        operatePanel.setBounds(7, yOffset, 600, 50);
        filePathPanel.setBounds(21, 50 + yOffset, 600, 50);
        filenamePanel.setBounds(21, 100 + yOffset, 600, 50);
        imageWidthPanel.setBounds(21, 150 + yOffset, 600, 50);

        JPanel panel1 = new JPanel();
        panel1.setLayout(null);
        panel1.add(operatePanel);
        panel1.add(filePathPanel);
        panel1.add(filenamePanel);
        panel1.add(imageWidthPanel);
        cardPanel.add(panel1, "长图生成");

        JPanel panel2 = new JPanel();
        panel2.add(new JLabel("ssss"));
        cardPanel.add(panel2, "台词拼图");

        this.add(cardPanel);
        this.setVisible(true);
    }

    private BufferedImage getFrameIcon() {

        URL resource = this.getClass().getClassLoader().getResource("marine_tiny_icon.png");
        if (resource == null) {
            return null;
        }

        BufferedImage image = null;
        try {
            image = ImageIO.read(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        BufferedImage icon = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = icon.createGraphics();

        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.drawImage(image, 0, 0, 64, 64, Color.RED, null);
        g.dispose();
        return icon;
    }

    private void handleGenLongImage() {
        // 获取表单的值
        List<Component> components = context.getFormComponents();
        components.parallelStream().filter(component -> "filenameTextField".equals(component.getName()))
                .findFirst().ifPresent(component -> context.getGenImageModel().setFilename(((JTextField) component).getText()));
        components.parallelStream().filter(component -> "imageWidthTextField".equals(component.getName()))
                .findFirst().ifPresent(component -> context.getGenImageModel().setMaxWidth(Integer.parseInt(((JTextField) component).getText())));

        GenImageModel model = context.getGenImageModel();

        int width = model.getMaxWidth();
        if (width <= 0) {
            JOptionPane.showMessageDialog(this, "图片宽度必须大于0", "Oops!", JOptionPane.WARNING_MESSAGE);
            return;
        }

        File[] imageLocations = model.getFiles();
        if (imageLocations == null || imageLocations.length <= 0) {
            LOGGER.warning("no input images.");
            JOptionPane.showMessageDialog(this, "请选择要拼接的图片", "Oops!", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        String outputPath;
        if (model.getFilePath() != null && model.getFilePath().length() > 0) {
            boolean b = new File(model.getFilePath()).mkdirs();
            outputPath = model.getFilePath() + "/" + model.getFilename();
        } else {
            outputPath = model.getFilename();
        }
        File file = new File(outputPath);
        outputPath = file.getPath();
        if (file.isDirectory()) {
            JOptionPane.showMessageDialog(this, "路径有误：" + outputPath, "Oops!", JOptionPane.WARNING_MESSAGE);
            return;
        }

        if (file.exists()) {
            int i = JOptionPane.showConfirmDialog(this,
                    "文件 " + outputPath + " 已存在，是否覆盖？", "Oops!", JOptionPane.YES_NO_OPTION);
            if (JOptionPane.YES_OPTION != i) {
                return;
            }
        }

        try {
            List<BufferedImage> images = Arrays.stream(imageLocations).parallel().map(this::loadImage)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            double height = 0;
            for (BufferedImage image : images) {
                height = (double) width / (double) image.getWidth() * (double) image.getHeight() + height;
            }

            double offset = 0;
            List<DrawImageInfo> drawImageInfoList = new ArrayList<>(images.size());
            for (BufferedImage image : images) {
                double h = (double) width / (double) image.getWidth() * (double) image.getHeight();
                DrawImageInfo imageInfo = new DrawImageInfo();
                imageInfo.width = width;
                imageInfo.height = (int) h;
                imageInfo.offset = (int) offset;
                imageInfo.imgSource = image;
                drawImageInfoList.add(imageInfo);
                offset += h;
            }

            BufferedImage card1 = new BufferedImage(width, (int) height, BufferedImage.TYPE_INT_RGB);
            Graphics2D graphics = card1.createGraphics();
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

            long start = System.currentTimeMillis();
/*            try {
                for (DrawImageInfo image : drawImageInfoList) {
                    graphics.drawImage(image.imgSource, 0, image.offset, image.width, image.height, this);
                }
            } finally {
                graphics.dispose();
            }*/


            ExecutorService executor = Executors.newFixedThreadPool(Math.min(drawImageInfoList.size(), 100));
            try {
                List<CompletableFuture<Boolean>> futures2 = drawImageInfoList.stream().map(item ->
                        CompletableFuture.supplyAsync(() -> graphics.drawImage(item.imgSource, 0, item.offset, item.width, item.height, this), executor))
                        .collect(Collectors.toList());
                CompletableFuture<Void> allFutures2 = CompletableFuture.allOf(futures2.toArray(new CompletableFuture[0]));
                allFutures2.thenApply(v -> futures2.stream().map(CompletableFuture::join).collect(Collectors.toList())).join();
            } finally {
                graphics.dispose();
                executor.shutdown();
            }

            System.out.println(System.currentTimeMillis() - start);

//            ImageIO.write(card1, decideImageFormat(outputPath), new BufferedOutputStream(new FileOutputStream(file)));
//            ImageIO.write(card1, decideImageFormat(outputPath), new FileOutputStream(file));
            ImageIO.write(card1, decideImageFormat(outputPath), file);
            System.out.println(System.currentTimeMillis() - start);
            LOGGER.info("Image create successful!, see " + file.getAbsolutePath());

            String[] options = {"打开查看", "关闭"};
            int i = JOptionPane.showOptionDialog(this, "已保存至" + file.getAbsolutePath(), "成功啦！",
                    JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
            if (i == 0) {
                Runtime.getRuntime().exec("explorer.exe /select," + outputPath);
            }
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "生成失败, " + e.getLocalizedMessage(), "出错啦！", JOptionPane.ERROR_MESSAGE);
        } finally {
            // 保存上次执行配置
            context.getConfigHelper().storageConfig(model);
        }
    }

    private BufferedImage loadImage(File location) {
        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(location))) {
            return ImageIO.read(in);
        } catch (IOException e) {
            LOGGER.severe(e.getLocalizedMessage() + ": " + location);
            throw new RuntimeException(e);
        }
    }

    private static String decideImageFormat(String outputPath) {
        int idx = outputPath.lastIndexOf(".");
        if (idx == -1) {
            return "png";
        }
        String suffixFileName = outputPath.substring(idx + 1);
        if ("png".equalsIgnoreCase(suffixFileName)) {
            return suffixFileName;
        }
        if ("jpeg".equalsIgnoreCase(suffixFileName) || "jpg".equalsIgnoreCase(suffixFileName)) {
            return "jpeg";
        }
        return "png";
    }

    private void handleChooseImages(JLabel genLabel) {
        JFileChooser fc = new JFileChooser(context.getGenImageModel().getFilePath());
        fc.setDialogTitle("选择图片");
        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        fc.setMultiSelectionEnabled(true);
        fc.setFileFilter(new FileFilter() {
            private final List<String> acceptFormat = Arrays.asList("png", "gif", "jpeg", "jpg", "tif", "tga", "bmp");

            @Override
            public boolean accept(File file) {
                String path = file.getName().toLowerCase();
                if (file.isDirectory()) {
                    return true;
                }
                return acceptFormat.stream().anyMatch(path::endsWith);
            }

            @Override
            public String getDescription() {
                StringBuilder sb = new StringBuilder("(");
                for (int i = 0; i < acceptFormat.size(); i++) {
                    String fmt = acceptFormat.get(i);
                    sb.append("*.").append(fmt);
                    if (i != acceptFormat.size() - 1) {
                        sb.append(",");
                    }
                }
                sb.append(")");
                return "图片类型" + sb.toString();
            }
        });

        int flag = fc.showOpenDialog(this);

        if (flag == JFileChooser.APPROVE_OPTION) {
            File[] imageLocations = fc.getSelectedFiles();
            if (imageLocations.length <= 0) {
                LOGGER.warning("no input images.");
                return;
            }
            context.getGenImageModel().setFiles(imageLocations);
        }
        File[] files = context.getGenImageModel().getFiles();
        if (files == null || files.length <= 0) {
            genLabel.setText("");
        } else {
            genLabel.setText("已选择" + files.length + "张图片");
        }

    }

    class DrawImageInfo {
        public BufferedImage imgSource;
        public int width;
        public int height;
        public int offset;
    }

    class NumberDocument extends PlainDocument {
        private static final long serialVersionUID = 6883132138538410506L;

        public NumberDocument() {
        }

        @Override
        public void insertString(int var1, String var2, AttributeSet var3) throws BadLocationException {
            if (this.isNumeric(var2)) {
                super.insertString(var1, var2, var3);
            } else {
                Toolkit.getDefaultToolkit().beep();
            }

        }

        private boolean isNumeric(String var1) {
            try {
                Long.valueOf(var1);
                return true;
            } catch (NumberFormatException var3) {
                return false;
            }
        }
    }

}
